package com.ultrapower.charts.delaunay
{
	import de.polygonal.ds.HashMap;
	import de.polygonal.ds.Iterator;
	import de.polygonal.ds.Set;
	
	import flash.display.Sprite;
	import flash.events.MouseEvent;
	
	import mx.core.UIComponent;
	/**
	 * DalaunayChart Flex component developed by liwenzhi(lwz7512)
	 * in Sep 2008 for hebei project to show basesite covering area.
	 * for production use, need to further modification.
	 */ 

	public class DelaunayChart extends UIComponent
	{
		
		public static var voronoiColor:uint = ColorUtils.magenta;
	    public static var delaunayColor:uint = ColorUtils.green;
	    public static var pointRadius:int = 3;
	
	    private var dt:Triangulation;                   // Delaunay triangulation
	    private var colorTable:HashMap;     			// Remembers colors for display
	    private var initialTriangle:Triangle;           // Initial triangle
	    private static var initialSize:int = 10000;     // Size of initial triangle
	    
	    private var clip:Sprite;
	    private var drawContainer:Sprite;
	  
		private var bgColorDrawed:Boolean;
		private var _bgColor:uint = 0xCCCCCC;
		
		//"manual", "auto"
		private var _drawMode:String = "manual";
		
		private var drawTarget:Sprite;
		
		private var _addMask:Boolean = false;
		//[{x:10,y:10},{x:400,y:150},{600,200}]
		private var _pointsToDraw:Array;
		
		
		public function DelaunayChart()
		{
			super();				
			createInitialTriangle();			
		}
		
		public function set drawMode(mode:String):void{
			_drawMode = mode;
			invalidateProperties();
			invalidateDisplayList();
		}
		public function set bgColor(bg:String):void{
			_bgColor = uint(Number(bg));
			invalidateDisplayList();
		}
		public function set dataProvider(arr:Array):void{
			_pointsToDraw = arr;
			
			for each(var eachPt:Object in arr){
				var mouseX:Number = eachPt.x;
				var mouseY:Number = eachPt.y;
				var pnt:Pnt = new Pnt(mouseX,mouseY);
				addSite(pnt);
			}
			invalidateDisplayList();
		}
		
		private function createInitialTriangle():void{
			var pntSet:Set = new Set();
			pntSet.setItem(new Pnt(-initialSize, -initialSize));
			pntSet.setItem(new Pnt( initialSize, -initialSize));
			pntSet.setItem(new Pnt(           0,  initialSize));
			initialTriangle = new Triangle(pntSet);
		    dt = new Triangulation(initialTriangle);
		}
		
		
		private function drawDelaunayChart(event:MouseEvent):void{
			var mouseX:Number = event.localX;
			var mouseY:Number = event.localY;
			var pnt:Pnt = new Pnt(mouseX,mouseY);
			addSite(pnt);
			
			invalidateDisplayList();
		}
		
		 /**
	     * Add a new site to the DT.
	     * @param point the site to add
	     */
	    public function addSite(point:Pnt):void {
	        dt.delaunayPlace(point);
	    }
	
	    /**
	     * Re-initialize the DT.
	     */
	    public function clear():void {
	        dt = new Triangulation(initialTriangle);
	    }
	
	    /**
	     * Get the color for the spcified item; generate a new color if necessary.
	     * @param item we want the color for this item
	     * @return item's color
	     */
	    private function getColor (item:Object):uint {
	        var color:uint = 0xFFFFFF * Number(Math.random().toFixed(2));
	        return color;
	    }
	
	    /* Basic Drawing Methods */
	
	    /**
	     * Draw a point.
	     * @param point the Pnt to draw
	     */
	    public function drawPoint (point:Pnt):void {
	        var r:int = pointRadius;
	        var x:int =  point.coord(0);
	        var y:int = point.coord(1);	        
	        drawTarget.graphics.drawCircle(x-r, y-r, r);	        
	    }
	
	    /**
	     * Draw a circle.
	     * @param center the center of the circle
	     * @param radius the circle's radius
	     * @param fillColor null implies no fill
	     */
	    public function drawCircle (center:Pnt, radius:Number, fillColor:uint):void {
	        var x:int = center.coord(0);
	        var y:int = center.coord(1);
	        var r:int = radius;	
	        if(fillColor){
	        	drawContainer.graphics.beginFill(fillColor,1); 
	        }
	               
	        drawContainer.graphics.drawCircle(x-r, y-r, r);
	        drawContainer.graphics.endFill();
	    }
	
	    /**
	     * Draw a polygon.
	     * @param polygon an array of polygon vertices
	     * @param fillColor null implies no fill
	     */
	    public function drawPolygon (polygon:Array, fillColor:uint):void{
	        var x:Array = new Array(polygon.length);
	        var y:Array = new Array(polygon.length);
	        for (var i:int = 0; i < polygon.length; i++) {
	        	var xpos:Number = Pnt(polygon[i]).coord(0);
	        	var ypos:Number = Pnt(polygon[i]).coord(1);
	            x[i] = int(xpos);
	            y[i] = int(ypos);
	        }

	        drawTarget.graphics.lineStyle(1,0x000000);
	        if(fillColor){
	        	drawTarget.graphics.beginFill(fillColor);
	        }
	        
	        drawTarget.graphics.moveTo(x[0],y[0]);
	        for(var j:int = polygon.length-1;j>=0;j--){
	        	drawTarget.graphics.lineTo(x[j],y[j]);
	        }
	        drawTarget.graphics.endFill();
	    }	    	    	  
	    
	    
	
	    /* Higher Level Drawing Methods */
	
	    
	    /**
	     * Draw all the Delaunay triangles.
	     * @param withFill true iff drawing Delaunay triangles with fill colors
	     */
	    public function drawAllDelaunay (withFill:Boolean):void {
	    	var iterator:Iterator = dt.iterator;
	    	while(iterator.hasNext()){
	    		var triangle:Triangle = iterator.next() as Triangle;
	    		var vertices:Array = triangle.toArray();
	    		var randomColor:uint = getColor(triangle);
	            drawPolygon(vertices, randomColor);
	    	}
	        
	    }
	
	    /**
	     * Draw all the Voronoi cells.
	     * @param withFill true iff drawing Voronoi cells with fill colors
	     * @param withSites true iff drawing the site for each Voronoi cell
	     */
	    public function drawAllVoronoi (withFill:Boolean, withSites:Boolean):void {
	    	
	    	drawTarget.graphics.clear();
	    	
	        // Keep track of sites done; no drawing for initial triangles sites
	        var done:Set = new Set();
	        done.addAll(initialTriangle);
	        var triIterator:Iterator = dt.iterator;
	        while(triIterator.hasNext()){
	        	var triangle:Triangle = triIterator.next() as Triangle;
	        	var siteIterator:Iterator = triangle.iterator;
	        	while(siteIterator.hasNext()){
	        		//each point added
	        		var site:Pnt = siteIterator.next() as Pnt;
	        		if(!done.contains(site)){
	        			done.setItem(site);
	        			var list:Array = dt.surroundingTriangles(site,triangle);
	        			var vertices:Array = new Array(list.length);
	        			var i:int = 0;
	        			for each(var tri:Triangle in list){
	        				vertices[i++] = tri.getCircumcenter();
	        			}
	        			var randomColor:uint = getColor(triangle);
	        			//draw the real polygon
	        			drawPolygon(vertices,randomColor);
	        			if(withSites){
	        				//draw the point added in chart!
	        				drawPoint(site);
	        			}
	        		}
	        		
	        	}
	        }
	        	        
	    }
	    
	   
	
	    /**
	     * Draw all the empty circles (one for each triangle) of the DT.
	     */
	    public function drawAllCircles ():void {
	        // Loop through all triangles of the DT
	        var iterator:Iterator = dt.iterator;
	        while(iterator.hasNext()){
	        	var triangle:Triangle = iterator.next();
	        	if(!triangle.containsAny(initialTriangle)){
	        		var c:Pnt = triangle.getCircumcenter();
	            	var radius:Number = c.subtract(triangle.getItemAt(0) as Pnt).magnitude();
	            	drawCircle(c, radius, 0);
	        	}
	        }
	       
	    }
	    /**
	    * override the Flex component implementation method
	    */
	    override protected function createChildren():void{
	    	super.createChildren();
	    	
	    	if(_drawMode=="manual"){	    		
	    		createMouseHandler();
	    		drawTarget = this;		
	    	}
	    	if(!_addMask && _drawMode=="auto"){	    		
	    		drawContainer = new Sprite();	    	
	    		this.addChild(drawContainer);	    		
	    			    			    			    	
	    		clip = new Sprite();	    			    	 	
	    		this.addChild(clip);
	    		
	    		drawContainer.mask = clip;	   
	    		
	    		drawTarget = drawContainer;	    			    		    			    
	    	}
	    }
	    
	    override protected function commitProperties():void{
	    	super.commitProperties();
	    		   
	    }
		
		
		
	    override protected function measure():void{
	    	super.measure();
	    	this.measuredHeight = 400;
	    	this.measuredWidth = 600;
	    }
	    
	    
	    override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void{
	    	super.updateDisplayList(unscaledWidth,unscaledHeight);	
	    	
	    	if(clip){
	    		clip.graphics.clear();    	    	
	    		clip.graphics.beginFill(0x000000);
	    		clip.graphics.drawRect(0,0,unscaledWidth,unscaledHeight);
	    	}	
	    	    		    
	    	if(_pointsToDraw){
	    		drawAllVoronoi(true,true);
	    	}else{	    		
	    		drawTarget.graphics.beginFill(_bgColor);
	    		drawTarget.graphics.drawRect(0,0,unscaledWidth,unscaledHeight);
	    	}
	    	
	    }
	    
		
		private function createMouseHandler():void{
			this.addEventListener(MouseEvent.CLICK,drawDelaunayChart);
		}
		
	}//end of DelaunayChart
}